package ai.minimax.algorithm;

import java.util.Observable;

import ai.minimax.Config;
import ai.minimax.entities.Action;
import ai.minimax.entities.CellState;
import ai.minimax.entities.Player;
import ai.minimax.entities.State;

public class Minimax extends Observable{
	
	private State currentGameState;
	private State stateInAlgo;
	int rekursionDepth=0;
	int recursionDepthCounter=0;
	int recursionDepthLimit=7;
	long startTime;
	
	public void setCurrentGameState(State gameState) {
		this.currentGameState = gameState;
		setChanged();
		notifyObservers(this.currentGameState);
	}

	public State getCurrentGameState() {
		return currentGameState;
	}

	public Minimax(){
		currentGameState = new State();
	}
	
	
	
	public Action minimaxDecision(){
		startTime = System.currentTimeMillis();
		stateInAlgo = currentGameState;
		int bestUtility = Integer.MIN_VALUE;
		Action bestAction = null;
		int utility;
		for(int row=0;row<Config.DIMENSION;row++){
			for(int col=0;col<Config.DIMENSION;col++){
				if(currentGameState.getField()[row][col] == CellState.EMPTY){
					Action action = new Action(row, col, Player.MAX);
					stateInAlgo.doAction(action);
					rekursionDepth++;
					utility = minValue();
					rekursionDepth--;
					if(utility > bestUtility){
						bestUtility = utility;
						bestAction = action;
					}
					stateInAlgo.undoAction(action);
				}
			}
		}
		System.out.println("Anzahl Knoten auf Rekursionstiefe: "+recursionDepthCounter);
		System.out.println("Gesamt-Laufzeit: "+(System.currentTimeMillis()-startTime));
		System.out.println("action player: "+bestAction.getPlayer());
		return bestAction;
	}
	
	private int minValue(){
		int utility = Integer.MAX_VALUE;
		if(stateInAlgo.terminalTest()){
			utility = stateInAlgo.getUtility();
		}else{
			for(int row=0;row<Config.DIMENSION;row++){
				for(int col=0;col<Config.DIMENSION;col++){
					if(currentGameState.getField()[row][col] == CellState.EMPTY){
						Action action = new Action(row, col, Player.MIN);
						stateInAlgo.doAction(action);
						rekursionDepth++;
						int tmp = maxValue();
						rekursionDepth--;
						utility = Math.min(tmp, utility);
						stateInAlgo.undoAction(action);
					}
				}
			}
		}
		if(rekursionDepth == recursionDepthLimit){
			recursionDepthCounter++;
			System.out.println("Teil-Laufzeit Knoten "+recursionDepthCounter+" in Rekursionstiefe "+recursionDepthLimit+" : "+(System.currentTimeMillis()-startTime));
		}
		return utility;
	}
	
	private int maxValue(){
		int utility = Integer.MIN_VALUE;
		if(stateInAlgo.terminalTest()){
			utility = stateInAlgo.getUtility();
		}else{
			for(int row=0;row<Config.DIMENSION;row++){
				for(int col=0;col<Config.DIMENSION;col++){
					if(currentGameState.getField()[row][col] == CellState.EMPTY){
						Action action = new Action(row, col, Player.MAX);
						stateInAlgo.doAction(action);
						rekursionDepth++;
						int tmp = minValue();
						rekursionDepth--;
						utility = Math.max(tmp, utility);
						stateInAlgo.undoAction(action);
					}
				}
			}
		}
		if(rekursionDepth == recursionDepthLimit){
			recursionDepthCounter++;
			System.out.println("Teil-Laufzeit Knoten "+recursionDepthCounter+" in Rekursionstiefe "+recursionDepthLimit+" : "+(System.currentTimeMillis()-startTime));
		}
		return utility;
	}

	public void doAction(Action action) {
		this.currentGameState.doAction(action);
		this.setChanged();
		this.notifyObservers(this.currentGameState);
	}
}
